#ifdef __GNUG__
#pragma implementation
#endif

#include "SunWindowPort.h"

#include "Class.h"
#include "SunBitmap.h"
#include "SunFont.h"
#include "String.h"
#include "Storage.h"
#include "Error.h"
#include "Math.h"
#include "OrdColl.h"
#include "Window.h"
#include "UNIX/UnixSystem.h"
#include "WindowBorder.h"
#include "File.h"
#include "ET_errno.h"

#include "pix.h"

#define MaxTextBatchCnt 400

#ifndef EWOULDBLOCK
#       define EWOULDBLOCK     35       /* Operation would block */
#endif

#define setbit(a,i) ((a)[(i) >> 5])|= 1 << (31-((i) & 31))

static pixrect *MakePen(int psz);
static bool myread(int fd, struct inputevent *ie);
static bool MakeQuadrant(int radx, int rady, int gapx, int gapy, int h);

static struct pr_prpos bat[MaxTextBatchCnt], *bp;
static Rectangle batchrect;
static int gapybits[50];
static int shallowsteep[100];
static int steepshallow[100];
static int lastdx, lastdy;


static struct pr_chain
	left1= { 0, { 0, 0 }, steepshallow },
	leftgapy= { &left1, { 0, 0 }, gapybits },
	left0= { &leftgapy, { 0, 0 }, shallowsteep },
	right1= { 0, { 0, 0 }, steepshallow },
	rightgapy= { &right1, { 0, 0 }, gapybits },
	right0= { &rightgapy, { 0, 0 }, shallowsteep };
				
static struct pr_fall left_oct = {
	{ 0, 0 },
	&left0
};
static struct pr_fall right_oct = {
	{ 0, 0 },
	&right0
};
		
struct pr_trap oval = {
	&left_oct,
	&right_oct,
	0,
	0
};

static struct rect sr;

inline struct rect *R2r(const Rectangle &r)
{
	sr.r_left= r.origin.x;
	sr.r_top= r.origin.y;
	sr.r_width= r.extent.x;
	sr.r_height= r.extent.y;
	return &sr;
}

#define unlock() if (!inbatch) pw_unlock(pw)

#define AlignX(xx) (pat ? ((xx)-origin.x-fso.x) % pat->pr_size.x : 0)
#define AlignY(yy) (pat ? ((yy)-origin.y-fso.y) % pat->pr_size.y : 0)
#define Align2X(xx) ((xx)-origin.x-fso.x)
#define Align2Y(yy) ((yy)-origin.y-fso.y)

#define Rop(r, mode, p, o) \
if (pr) \
	pr_rop(pr, (r).origin.x, (r).origin.y, \
		(r).extent.x, (r).extent.y, mode, p, o.x, o.y); \
else \
	pw_rop(pw, (r).origin.x, (r).origin.y, \
		(r).extent.x, (r).extent.y, mode, p, o.x, o.y);

#define Vector(x1, y1, x2, y2) \
if (pr) \
	pr_vector(pr, (x1), (y1), (x2), (y2), cmode, 1); \
else \
	pw_vector(pw, (x1), (y1), (x2), (y2), cmode, 1);

//---- SunInputHandler ---------------------------------------------------------

class SunInputHandler : public FileHandler {
	SunWindowPort *sp;
public:
	SunInputHandler(SunWindowPort *s, int fd) : FileHandler(fd, 1)
		{ sp= s; }
	bool ReadNotify()
		{ return sp->Notify(); }
};

//---- SunWindowPort -----------------------------------------------------------

NewMetaImpl0(SunWindowPort,WindowPort);

SunWindowPort::SunWindowPort(Window *bw, WindowType wt, GrCursor curs)
						    : WindowPort(bw, wt, curs)
{
	bool bl= FALSE, ov= FALSE;

	switch (wt) {
	case eWinPlain:
		break;
	case eWinStandard:
		break;
	case eWinDialog:
		break;
	case eWinPulldownMenu:
	case eWinPopupMenu:
		bl= ov= overlay= TRUE;
		break;
	case eWinIcon:
		break;
	}

	if (!overlay) {
		fd= win_getnewwindow();
		num= win_fdtonumber(fd);
		File::AddFdName(fd, form("/dev/win%d", num),
						"SunWindowPort::SunWindowPort");
		File::SetNonBlock(fd);
		win_setuserflag(fd, 0x10000000, 1);
	}
	if (! (ov && bl)) {
		inputhandler= new SunInputHandler(this, fd);
		gSystem->AddFileHandler(inputhandler);
	}
	opw= 0;
}

SunWindowPort::~SunWindowPort()
{
	if (inputhandler) {
		inputhandler->Remove();
		inputhandler= 0;
	}
	if (!overlay) {
		win_remove(fd);
		if (rpw)
			pw_close(rpw);
		if (opw)
			pw_close(opw);
		if (rpr)
			pr_close(rpr);
		if (pr)
			pr_close(pr);
		File::Close(fd, "SunWindowPort::~SunWindowPort");
	}
}

void SunWindowPort::TestDamage()
{
	if (overlay || opw == 0)
		return;
	struct rect s;
	win_getrect(fd, &s);
	pw_damaged(opw);
	if (s.r_width != portrect.extent.x || s.r_height != portrect.extent.y) {
		OpenNotify(Rectangle(s.r_left, s.r_top, s.r_width, s.r_height));
		pw_donedamaged(opw);
	} else {
		register struct rectnode *rn;
		for (rn= opw->pw_clipdata->pwcd_clipping.rl_head; rn; rn= rn->rn_next) {
			s= rn->rn_rect;
			if (s.r_width > 0 && s.r_height > 0)
				InvalidateRect(Rectangle(s.r_left, s.r_top, s.r_width, s.r_height), TRUE);
		}
		pw_donedamaged(opw);
	}
}

pixrect *SunWindowPort::GetPattern(const Point &ee)
{
	if (patbm) {
		static pixrect *patternpr= 0;
		Point ss(patbm->Size());
		Point e(ee);

		if (0 && (ss.x & 0x07) == 0 && (ss.y & 0x07) == 0) {
			e.x= ((e.x >> 5)+1) << 5;
			e.y= ((e.y >> 5)+1) << 5;

			NewPixrect(&patternpr, e, FALSE, FALSE, patbm->Depth());
			pr_replrop(patternpr, 0, 0, patternpr->pr_size.x, patternpr->pr_size.y,
									PIX_SRC | PIX_DONTCLIP, patbm->pr, 0, 0);
			pat= patternpr;
		} else {
			Point mul(1);
			if (mul.x > 1 || mul.y > 1) {
				register int x, y;
				NewPixrect(&patternpr, ss*mul, FALSE, FALSE, patbm->Depth());
				for (y= 0; y < mul.y; y++)
					for (x= 0; x < mul.x; x++)
						pr_rop(patternpr, x*ss.x, y*ss.y, ss.x, ss.y,
								PIX_SRC | PIX_DONTCLIP, patbm->pr, 0, 0);
				pat= patternpr;
			} else {
				pat= patbm->pr;
			}
		}
	}
	return pat;
}

void SunWindowPort::Stencil(const Rectangle &r, pixrect *ppr)
{
	lock(r);
	if (color) {
		Rop(r, PIX_ERASE, ppr, gPoint0);
		Rop(r, PIX_DST | tmode, ppr, gPoint0);
	} else if (patbm) {
		if (pr)
			pr_stencil(pr, r.origin.x, r.origin.y, r.extent.x, r.extent.y,
				tmode, ppr, 0, 0, GetPattern(r.extent), AlignX(r.origin.x), AlignY(r.origin.y));
		else
			pw_stencil(pw, r.origin.x, r.origin.y, r.extent.x, r.extent.y,
				tmode, ppr, 0, 0, GetPattern(r.extent), AlignX(r.origin.x), AlignY(r.origin.y));
	} else {
		Rop(r, tmode, ppr, gPoint0);
	}
	unlock();
}

void SunWindowPort::line(int psz, int x0, int y0, int x1, int y1)
{
	int w= psz/2;

	if (psz <= 1 && pat == 0) {
		Vector(x0, y0, x1, y1);
		return;
	}
	if (x0 == x1) {
		if (y0 <= y1)
			BitBlt(x0-w, y0, psz, y1-y0);
		else
			BitBlt(x1-w, y1, psz, y0-y1);
	} else if (y0 == y1) {
		if (x0 <= x1)
			BitBlt(x0, y0-w, x1-x0, psz);
		else
			BitBlt(x1, y1-w, x0-x1, psz);
	} else if (psz <= 1)
		Bresenham(0, x0, y0, x1, y1, psz);
	else {
		int n= 4;
		pr_pos pts[4];
		Point d= PolarToPoint(Phi(Point(x1-x0, y1-y0)), (double)psz/2, (double)psz/2);
		pts[0].x= x0 - d.y;
		pts[0].y= y0 + d.x;
		pts[1].x= x0 + d.y;
		pts[1].y= y0 - d.x;
		pts[2].x= x1 + d.y;
		pts[2].y= y1 - d.x;
		pts[3].x= x1 - d.y;
		pts[3].y= y1 + d.x;
		if (pr)
			pr_polygon_2(pr, 0, 0, 1, &n, pts, cmode, pat, AlignX(0), AlignY(0));
		else
			pw_polygon_2(pw, 0, 0, 1, &n, pts, cmode, pat, AlignX(0), AlignY(0));
	}
}

void SunWindowPort::PolyDot(pixrect *pr1, int dx, int dy, pr_pos *pts, int n, int psz)
{
	register int i, x, y, v;
	register pr_pos *p;
	register pixrect *patpr;

	patpr= pat;

	if (pr1)
		patpr= 0;
	else if (pr)
		pr1= pr;

	if (psz <= 1) {
		if (patpr) {
			for (p= pts, i= 0; i<n; i++, p++) {
				x= p->x + dx;
				y= p->y + dy;
				v= pr_get(pat, AlignX(x), AlignY(y));
				if (pr1)
					pr_put(pr1, x, y, v);
				else
					pw_put(pw, x, y, v);
			}
		} else {
#if !defined(sparc)    /* Circumvent GFX (sparc) bug */
			if (pr1)
				pr_polypoint(pr1, dx, dy, n, pts, cmode);
			else
				pw_polypoint(pw, dx, dy, n, pts, cmode);
#else /* sparc */
			for (p= pts, i= 0; i < n; i++, p++) {
				x= p->x + dx;
				y= p->y + dy;
				if (pr1)
					pr_rop(pr1, x, y, 1, 1, cmode, 0, 0, 0);
				else
					pw_rop(pw, x, y, 1, 1, cmode, 0, 0, 0);
			}
#endif /* sparc */
		}
	} else {
		register pixrect *penpr= 0;
		int rop= cmode;
		
		if (psz >= 2 && psz < MAXPENS) {
			if (SunWindowSystem::pens[psz] == 0)
				SunWindowSystem::pens[psz]= MakePen(psz);
			penpr= SunWindowSystem::pens[psz];
			rop= PIX_OR;
		}
		for (p= pts, i= 0; i < n; i++, p++) {
			if (pr1)
				pr_rop(pr1, p->x+dx, p->y+dy, psz, psz, rop, penpr, 0, 0);
			else
				pw_rop(pw, p->x, p->y, psz, psz, rop, penpr, 0, 0);
		}
	}
}

void SunWindowPort::Bresenham(pixrect *pr1, int fromX, int fromY, int X2, int Y2, int psz)
{
	register int i, j, k, p, q, d, dx, dy;
	pr_pos *pts, *pp;

	i= (fromX < X2) ? 1 : -1;
	j= (fromY < Y2) ? 1 : -1;
	dx= Math::Abs(fromX - X2);
	dy= Math::Abs(fromY - Y2);

	pp= pts= (pr_pos*) alloca(sizeof(pr_pos) * (dx+dy+1));

	if (dx >= dy) { /* flat line */
		d= p= dy+dy;
		q= 2*(dy-dx);
		for(k= 0; k <= dx; fromX+= i, k++) {
			pp->x= fromX;
			pp++->y= fromY;
			if(d <= dx)
				d+= p;
			else {
				d+= q;
				fromY+= j;
			}
		}
	} else {          /* steep line */
		d= p= dx+dx;
		q= 2*(dx-dy);
		for(k= 0; k <= dy; fromY+= j, k++) {
			pp->x= fromX;
			pp++->y= fromY;
			if (d <= dy)
				d+= p;
			else {
				d+= q;
				fromX+= i;
			}
		}
	}
	PolyDot(pr1, 0, 0, pts, k, psz);
}

void SunWindowPort::PolyEllipse(pixrect *pr1, int x0, int y0, int x1, int x2,
										int y1, int y2, int a, int b, int psz)
{
	register pr_pos *qp;
	register int x, y, d, t6, t7;
	int t1, t2, t3, t4, t5, t8;
	pr_pos *q, *qe;

	x= a;
	y= 0;
	t1= a*a; t2= t1 << 1;
	t3= b*b; t4= t3 << 1;
	t5= a * t3; t6= t5 << 1; t7= 0;
	t8= a + b;
	qp= q= (pr_pos*) alloca(sizeof (pr_pos) * t8);
	qe= &q[t8];
	d= (t1+t3)/4-t5;

	while (qp < qe) {
		qp->x= x;
		qp++->y= y;
		if (d < 0) {
			y++;
			t7+= t2;
			d+= t7;
		} else {
			x--;
			t6-= t4;
			d-= t6;
		}
	}

	x1+= x0; x2+= x0;
	y1+= y0; y2+= y0;

	PolyDot(pr1, x1, y2, q, t8, psz);
	for (qp= q; qp < qe; qp++)
		qp->x= -qp->x;
	PolyDot(pr1, x2, y2, q, t8, psz);
	for (qp= q; qp < qe; qp++)
		qp->y= -qp->y;
	PolyDot(pr1, x2, y1, q, t8, psz);
	for (qp= q; qp < qe; qp++)
		qp->x= -qp->x;
	PolyDot(pr1, x1, y1, q, t8, psz);
}

void SunWindowPort::lock(const Rectangle &r)
{
	FlushColorMap();
	if (! inbatch)
		pw_lock(pw, R2r(r));
}

void SunWindowPort::BitBlt(int x, int y, int w, int h)
{
	if (pat) {
		if (pr)
			pr_replrop(pr, x, y, w, h, cmode, pat, Align2X(x), Align2Y(y));
		else
			pw_replrop(pw, x, y, w, h, cmode, pat, Align2X(x), Align2Y(y));
	} else {
		if (pr)
			pr_rop(pr, x, y, w, h, cmode, 0, 0, 0);
		else
			pw_rop(pw, x, y, w, h, cmode, 0, 0, 0);
	}
}

void SunWindowPort::DevClip(const Rectangle &r)
{
	if (WindowSystem::fullscreen) {   // reset clipping
		if (pr) {
			pr= opr;
			fso= gPoint0;
		}
		if (! overlay)
			pw= opw;
	} else {
		if (pr) {
			if (rpr)
				pr_close(rpr);
			pr= rpr= pr_region(opr, r.origin.x, r.origin.y, r.extent.x, r.extent.y);
			fso= -r.origin; // simulate missing function pr_set_xy_offset()
		}
		if (! overlay) {
			if (rpw) {
				pw_set_region_rect(rpw, R2r(r), 1, 0);
			} else
				rpw= pw_region(opw, r.origin.x, r.origin.y, r.extent.x, r.extent.y);
			pw= rpw;
			pw_set_xy_offset(pw, r.origin.x, r.origin.y);
		}
	}
}

void SunWindowPort::DevStrokeLine2(int psz, const Rectangle &r, GrLineCap, const Point &p1, const Point &p2)
{
	if (psz < 0)
		return;
	if (psz == 0)
		psz= 1;
	lock(r);
	line(psz, p1.x, p1.y, p2.x, p2.y);
	unlock();
}

void SunWindowPort::DevStrokeRect2(int psz, const Rectangle &r)
{
	register int l= r.origin.x, t= r.origin.y, wd= r.extent.x, ht= r.extent.y;

	if (psz < 0)
		return;
	if (psz == 0)
		psz= 1;
	lock(r);
	BitBlt(l+psz, t, wd-psz, psz);
	BitBlt(l+wd-psz, t+psz, psz, ht-psz);
	BitBlt(l, t+ht-psz, wd-psz, psz);
	BitBlt(l, t, psz, ht-psz);
	unlock();
}

void SunWindowPort::DevFillRect(const Rectangle &r)
{
	lock(r);
	if (pat) {
		if (pr)
			pr_replrop(pr, r.origin.x, r.origin.y, r.extent.x, r.extent.y,
					cmode, pat, Align2X(r.origin.x), Align2Y(r.origin.y));
		else
			pw_replrop(pw, r.origin.x, r.origin.y, r.extent.x, r.extent.y,
					cmode, pat, Align2X(r.origin.x), Align2Y(r.origin.y));
	} else {
		Rop(r, cmode, 0, gPoint0);
	}
	unlock();
}

void SunWindowPort::DevStrokeRRect2(int psz, const Rectangle &r, const Point &dia)
{
	int ww, hh, cx, cy, w, h, rad1, rad2, l, t, r1, r2;
	pixrect *cpr= 0;
		
	if (psz < 0)
		return;
	if (psz == 0)
		psz= 1;
		
	ww= r.extent.x;
	hh= r.extent.y;

	w= ww - psz;
	h= hh - psz;

	rad1= (dia.x - psz)/2;
	rad2= (dia.y - psz)/2;
	rad1= Math::Max(1, rad1);
	rad2= Math::Max(1, rad2);

	r1= dia.x/2 -1;
	r2= dia.y/2;

	cx= ww-r1-r1;
	cy= hh-r2-r2;

	if (psz > 1) {
		if (! findpr('a', &cpr, ww, hh, rad1, rad2, psz)) {
			PolyEllipse(cpr, 0, 0, w-rad1, rad1, rad2, h-rad2, rad1, rad2, psz);
			if (cx) {
				pr_rop(cpr, r1, 0, cx, psz, PIX_SET|PIX_DONTCLIP, 0, 0, 0);
				pr_rop(cpr, r1, h, cx, psz, PIX_SET|PIX_DONTCLIP, 0, 0, 0);
			}
			if (cy) {
				pr_rop(cpr, 0, r2, psz, cy, PIX_SET|PIX_DONTCLIP, 0, 0, 0);
				pr_rop(cpr, w, r2, psz, cy, PIX_SET|PIX_DONTCLIP, 0, 0, 0);
			}
		}
		Stencil(r, cpr);
	} else {
		lock(r);
		l= r.origin.x;
		t= r.origin.y;
		PolyEllipse(0, l, t, w-rad1, rad1, rad2, h-rad2, rad1, rad2, psz);
		if (cx) {
			BitBlt(l+r1, t,   cx, psz);
			BitBlt(l+r1, t+h, cx, psz);
		}
		if (cy) {
			BitBlt(l,   t+r2, psz, cy);
			BitBlt(l+w, t+r2, psz, cy);
		}
		unlock();
	}
}

void SunWindowPort::DevFillRRect2(const Rectangle &r, const Point &dia)
{
	Point rad(dia/2), gap(r.extent - 2*rad);

	MakeQuadrant(rad.x, rad.y, gap.x, gap.y, r.extent.y);
	lock(r);
	if (pr)
		pr_traprop(pr, r.origin.x+rad.x, r.origin.y, oval, cmode,
			GetPattern(r.extent), rad.x+AlignX(r.origin.x), AlignY(r.origin.y));
	else
		pw_traprop(pw, r.origin.x+rad.x, r.origin.y, oval, cmode,
			GetPattern(r.extent), rad.x+AlignX(r.origin.x), AlignY(r.origin.y));

	unlock();
}

void SunWindowPort::DevStrokePolygon2(const Rectangle &r, Point *p, int n, GrPolyType, int psz, GrLineCap)
{
	register int i, dx, dy, x, y, x0, y0, x1, y1, ox, oy;
	bool xormode= (tmode == (PIX_SRC ^ PIX_DST));
	pixrect *pr1= 0;
	Rectangle rr;

	if (psz < 0)
		return;
	if (psz == 0)
		psz= 1;

	if (psz > 1 || pat != 0) {
		rr= r.Expand(psz/2+1);
		NewPixrect(&SunWindowSystem::tmppr, rr.extent, FALSE, TRUE, 1);
		pr1= SunWindowSystem::tmppr;
		ox= oy= 0;
	} else {
		ox= r.origin.x;
		oy= r.origin.y;
		lock(r);
	}

	x0= p[0].x + ox;
	y0= p[0].y + oy;

	for (i= 1; i < n; i++) {
		x= x1= p[i].x + ox;
		y= y1= p[i].y + oy;
		
		if (xormode && i < n-1) {
			dx= x1-x0;
			dy= y1-y0;
			if (dx >= Math::Abs(dy))
				x--;
			else if (-dx >= Math::Abs(dy))
				x++;
			if (dy >= Math::Abs(dx))
				y--;
			else if (-dy >= Math::Abs(dx))
				y++;
		}
		if (pr1 == 0) {
			Vector(x0, y0, x1, y1);
		} else
			Bresenham(pr1, x0, y0, x, y, psz);
		x0= x1;
		y0= y1;
	}
	if (pr1)
		Stencil(rr, pr1);
	else
		unlock();
}

void SunWindowPort::DevFillPolygon2(const Rectangle &r, Point *pts, int npts, GrPolyType)
{
	lock(r);
	if (pr)
		pr_polygon_2(pr, r.origin.x, r.origin.y, 1, &npts, (pr_pos*) pts,
				cmode, pat, AlignX(r.origin.x), AlignY(r.origin.y));
	else
		pw_polygon_2(pw, r.origin.x, r.origin.y, 1, &npts, (pr_pos*) pts,
				cmode, pat, AlignX(r.origin.x), AlignY(r.origin.y));
	unlock();
}

void SunWindowPort::DevShowBitmap(const Rectangle &r, Bitmap *bm)
{
	SunBitmap *dbm= (SunBitmap*) bm->GetDevBitmap();
	SunBitmap *sbm= (SunBitmap*) dbm->PrepareBitmap(this, r.extent, bm->GetColorMapper());
		
	if (sbm->depth > 1) {
		SunBitmap *mbm= (SunBitmap*) dbm->GetMask(r.extent, bm->GetColorMapper());
		if (mbm) {
			Rop(r, PIX_ERASE, mbm->pr, gPoint0);
			Rop(r, PIX_SRC | PIX_DST, sbm->pr, gPoint0);
		} else {
			Rop(r, PIX_SRC, sbm->pr, gPoint0);
		}
	} else
		Stencil(r, sbm->pr);
}

void SunWindowPort::DevShowChar(Font *fdp, const Point&, Rune c, const Rectangle &r)
{
	register int dx, dy;

	if (bp == 0)
		bp= bat;
	if (bp-bat >= MaxTextBatchCnt)
		FlushAnyText();

	register struct pixchar *pcc= ((SunFont*)fdp)->GetPixchar(c);
	bp->pr= pcc->pc_pr;
	dx= r.origin.x + pcc->pc_home.x;
	dy= r.origin.y + pcc->pc_home.y + fdp->Ascender();
	bp->pos.x= dx - lastdx;
	bp++->pos.y= dy - lastdy;
	lastdx= dx;
	lastdy= dy;
}

void SunWindowPort::DevShowTextBatch(const Rectangle &r, const Point&)
{
	int n;

	if (bp && (n= bp-bat) > 0) {
		bat[0].pos.x-= r.origin.x;
		bat[0].pos.y-= r.origin.y;
		
		if (patbm || color) {
			NewPixrect(&SunWindowSystem::tmppr, r.extent, FALSE, TRUE, 1);
			pr_batchrop(SunWindowSystem::tmppr, 0, 0, PIX_OR|PIX_DONTCLIP, bat, n);
			Stencil(r, SunWindowSystem::tmppr);
		} else {
			lock(r);
			if (pr)
				pr_batchrop(pr, r.origin.x+fso.x, r.origin.y+fso.y, tmode, bat, n);
			else
				pw_batchrop(pw, r.origin.x, r.origin.y, tmode, bat, n);
			unlock();
		}
		bp= bat;
		lastdx= lastdy= 0;
	}
}

void SunWindowPort::DevGiveHint(int code, int, void *vp)
{
	switch (code) {
	case eHintBatch:
		batchrect= *((Rectangle*) vp);
		if (! WindowSystem::batch) {
			lock(batchrect);
			break;
		}
		if (!inbatch && !overlay) {
			inbatch= TRUE;
			NewPixrect(&SunWindowSystem::tmppr2, batchrect.origin + batchrect.extent, FALSE,
											FALSE, GetDepth());
			pr= opr= SunWindowSystem::tmppr2;
			fso= gPoint0;
		}
		break;
		
	case eHintUnbatch:
		if (! WindowSystem::batch) {
			unlock();
			break;
		}
		if (inbatch && !overlay) {
			inbatch= FALSE;
			pw_rop(pw, batchrect.origin.x, batchrect.origin.y,
				batchrect.extent.x, batchrect.extent.y, PIX_SRC,
					SunWindowSystem::tmppr2, batchrect.origin.x, batchrect.origin.y);
			pr= opr= 0;
			fso= gPoint0;
		}
		break;
		
	case eHintCacheEnd:
		{
			batchInfo *bi= (batchInfo*) vp;
			SunBitmap *sbm= (SunBitmap*) bi->b;
			NewPixrect(&sbm->pr, bi->r.extent, FALSE, FALSE, sbm->Depth());
			pr_rop(sbm->pr, 0, 0, bi->r.extent.x, bi->r.extent.y, PIX_SRC, pr,
						bi->r.origin.x, bi->r.origin.y);
		}
		break;
		
	case eHintCacheStart:
		{
			batchInfo *bi= (batchInfo*) vp;
			Rop(bi->r, PIX_SRC, ((SunBitmap*)bi->b)->pr, bi->p);
		}
		break;
		
	case 66666: // DashStart
	case 77777: // RotateBegin
		ignore= TRUE;
		break;
	case 66667: // DashEnd
	case 77778: // RotateBegin
		ignore= FALSE;
		break;
		
	default:
		break;
	}
}

void SunWindowPort::DevSetCursor(GrCursor c)
{
	int ic= (int) c;
	struct _cursor curs;

	if (c == eCrsLeftRightArrow)
		ic= 18;
	else if (c == eCrsUpDownArrow)
		ic= 19;

	if (ic <= 0) {
		curs.cur_function= PIX_SRC;
		curs.cur_shape= 0;
		curs.cur_yhot= curs.cur_xhot= 0;
	} else {
		curs.cur_function= PIX_OR;
		curs.cur_xhot= -SunWindowSystem::cursors->pf_char[ic-1].pc_home.x;
		curs.cur_yhot= -SunWindowSystem::cursors->pf_char[ic-1].pc_home.y;
		curs.cur_shape= SunWindowSystem::cursors->pf_char[ic-1].pc_pr;
	}
	curs.flags= 0;
	_sunwindow_setcursor(fd, &curs);
}

void SunWindowPort::DevFullscreen(bool m)
{
	if (overlay)
		return;

	if (m) {
		if ((SunWindowSystem::FS= fullscreen_init(fd)) == NULL)
			Fatal("SunWindowPort::Fullscreen", "can't get fullscreen access");
		pw= SunWindowSystem::FS->fs_pixwin;
	} else {
		fullscreen_destroy(SunWindowSystem::FS);
		pw= opw;
		SunWindowSystem::FS= 0;
	}
}

void SunWindowPort::DevScrollRect(const Rectangle &r, const Point &p)
{
	register int l= r.origin.x, t= r.origin.y;
	bool hard= FALSE;

	if (hard= (p.x != 0 && p.y != 0))
		NewPixrect(&SunWindowSystem::tmppr2, r.extent, FALSE, FALSE, GetDepth());

	lock(cliprect);
	if (overlay) {
		if (hard) {
			pr_rop(SunWindowSystem::tmppr2, 0, 0, r.extent.x, r.extent.y, PIX_SRC, pr, l-p.x,t-p.y);
			pr_rop(pr, l, t, r.extent.x, r.extent.y, PIX_SRC, SunWindowSystem::tmppr2, 0, 0);
		} else
			pr_rop(pr, l, t, r.extent.x, r.extent.y, PIX_SRC, pr, l-p.x, t-p.y);
	} else {
		register struct rectnode *rlp;
		struct rect rr;
		
		if (hard) {
			pw_read(SunWindowSystem::tmppr2, 0, 0, r.extent.x, r.extent.y, PIX_SRC, pw, l-p.x, t-p.y);
			pw_rop(pw, l, t, r.extent.x, r.extent.y, PIX_SRC, SunWindowSystem::tmppr2, 0, 0);
		} else
			pw_copy(pw, l, t, r.extent.x, r.extent.y, PIX_SRC, pw, l-p.x, t-p.y);

		for (rlp= pw->pw_fixup.rl_head; rlp; rlp= rlp->rn_next) {
			rr= rlp->rn_rect;
			rr.r_left+= l;
			rr.r_top+= t;
			if (p.x > 0)
				rr.r_left-= p.x;
			if (p.y > 0)
				rr.r_top-= p.y;
			InvalidateRect(Rectangle(rr.r_left, rr.r_top, rr.r_width, rr.r_height));
		}
	}
	unlock();
}

void SunWindowPort::DevShow(WindowPort *FP, const Rectangle &er)
{
	SunWindowPort *fp= (SunWindowPort*) FP;
	struct rect r= *R2r(er), fsr, rr;

	gWindowSystem->Update();

	fatherport= fp;

	if (wcmap == 0) {
		wcmap= WindowSystem::wcmap;
		wcmap->Ref();
	}

	SunWindowSystem::overlayport= 0;
	if (! overlay) {
		portrect.extent= gPoint_1;
		win_setrect(fd, &r);

		/*
		* reset the links
		*/
		int topmostNo= win_getlink(SunWindowSystem::rootFd, WL_TOPCHILD);
		win_setlink(fd, WL_ENCLOSING, SunWindowSystem::parentNo);
		win_setlink(fd, WL_COVERED, topmostNo);
		win_setlink(fd, WL_COVERING, WIN_NULLLINK);
		win_setlink(fd, WL_BOTTOMCHILD, WIN_NULLLINK);
		win_setlink(fd, WL_TOPCHILD, WIN_NULLLINK);
		win_insert(fd);

		opw= pw= pw_open(fd);
		depth= pw->pw_pixrect->pr_depth;

		DevSetCursor(cursor);
		if (wcmap) {
			wcmap->changed= TRUE;
			wcmap->Install(this);
		}
		_sunwindow_setmask(fd);

		/*
		while (state != eWsShown)
			((UnixSystem*)gSystem)->DispatchSignals(FALSE);
		*/
		
		return;
	}
	if (fp == 0)
		Fatal("SunWindowPort::DevShow", "fatal error (fp == 0)");
	SunWindowSystem::overlayport= this;
	fd= fp->fd;
	num= fp->num;
	/* cursor= fp->cursor; */

	if (SunWindowSystem::FS == 0 || (fd != SunWindowSystem::FS->fs_windowfd))
		if ((SunWindowSystem::FS= fullscreen_init(fd)) == NULL)
			Fatal("SunWindowPort::DevShow", "can't get fullscreen access");

	pr= opr= pr_region(SunWindowSystem::FS->fs_pixwin->pw_pixrect, r.r_left, r.r_top, r.r_width, r.r_height);
	fso= gPoint0;

	rr= r;
	r.r_left+= SunWindowSystem::FS->fs_screenrect.r_left;
	r.r_top+= SunWindowSystem::FS->fs_screenrect.r_top;
	fsr= r;

	opw= pw= pw_region(SunWindowSystem::FS->fs_pixwin, r.r_left, r.r_top, r.r_width, r.r_height);
	depth= pw->pw_pixrect->pr_depth;
	inpOffset.x= -r.r_left;
	inpOffset.y= -r.r_top;

	fsr.r_left= fsr.r_top= 0;

	pw_lock(pw, &fsr);
	savepr= pw_save_pixels(fp->pw, &r);  /* now save the pixels */
	/* reset cursor (in lock -> cursor is invisible) */

	OpenNotify(Rectangle(rr.r_left, rr.r_top, rr.r_width, rr.r_height));

	if (fp->cursor != cursor)
		DevSetCursor(cursor);
	pw_unlock(pw);
}

void SunWindowPort::DevHide()
{
	if (overlay) {
		struct rect rr;
		struct SunWindowPort *fp= fatherport;
		
		/* restore the pixels */
		rr= savepr->r;
		rr.r_left= rr.r_top= 0;
		pw_lock(pw, &rr);
		pw_restore_pixels(pw, savepr);
		if (fp->overlay && (fp->cursor != cursor))
			DevSetCursor(fp->cursor);
		pw_unlock(pw);
		
		if (! fp->overlay) {
			fullscreen_destroy(SunWindowSystem::FS);
			SunWindowSystem::FS= 0;
		}

		if (rpw)
			pw_close(rpw);
		if (opw)
			pw_close(opw);
		rpw= pw= opw= 0;
		if (rpr)
			pr_close(rpr);
		if (opr)
			pr_close(opr);
		rpr= pr= opr= 0;
	} else
		win_remove(fd);
	SunWindowSystem::overlayport= 0;
	CloseNotify();
}

void SunWindowPort::DevIconize()
{
/*
	Hide();
	if (theIcon) {
		ic= theIcon;
		ic->OpenAt(GetRect().origin);
		SunWindowPort *swp= (SunWindowPort*) ic->GetPortDesc();
		swp->ic= win;
		theIcon= 0;
	}
	*/
	if (win) {
		win->Close();
		if (win->GetIcon())
			win->GetIcon()->OpenAt(win->GetRect().origin);
	}
}

void SunWindowPort::DevSetExtent(const Point &e)
{
	if (! overlay) {
		struct rect oldr, newr;
		win_getrect(fd, &oldr);
		newr= oldr;
		newr.r_width= e.x;
		newr.r_height= e.y;
		win_setrect(fd, &newr);
	}
}

void SunWindowPort::DevSetOrigin(const Point &o)
{
	struct rect oldr, newr, rr;
	struct rectlist rlold, rlnew, rld, rls;
	struct rectnode *rlp;

	rlold= rlnew= rld= rls= rl_null;

	win_getrect(fd, &oldr);
	newr= oldr;
	newr.r_left= o.x;
	newr.r_top= o.y;

	pw_exposed(opw);
	rl_copy(&opw->pw_clipdata->pwcd_clipping, &rlold);
	win_lockdata(fd);
	win_remove(fd);

	win_setrect(fd, &newr);

	win_insert(fd);
	win_computeclipping(fd);
	pw_exposed(opw);
	rl_copy(&opw->pw_clipdata->pwcd_clipping, &rlnew);
	rl_intersection(&rlold, &rlnew, &rls);
	rl_coalesce(&rls);
	rl_normalize(&rls);
	if (newr.r_left > oldr.r_left && newr.r_top < oldr.r_top)
		rl_sort(&rls, &rld, 0);
	else {
		if (newr.r_left > oldr.r_left || newr.r_top > oldr.r_top)
			rl_sort(&rls, &rld, 1);
		else
			rl_sort(&rls, &rld, 0);
	}

	rr= rect_bounding(&newr, &oldr);
	rr.r_left-= newr.r_left;
	rr.r_top-= newr.r_top;
	pw_lock(opw, &rr);
	for (rlp= rld.rl_head; rlp; rlp= rlp->rn_next) {
		struct rect *r= &rlp->rn_rect;
		fullscreen_pw_copy(opw, newr.r_left+r->r_left, newr.r_top+r->r_top,
				r->r_width, r->r_height,
				PIX_SRC, opw,
				oldr.r_left+r->r_left, oldr.r_top+r->r_top);
		win_partialrepair(fd, &rlp->rn_rect);
	}
	pw_unlock(opw);

	win_unlockdata(fd);
}

void SunWindowPort::DevTop(bool top)
{
	struct rectlist rlold, rlnew;
	struct rect rectnew;
	register struct rectnode *rn;

	rlold= rlnew= rl_null;

	win_lockdata(fd);

	pw_exposed(opw);
	rl_copy(&opw->pw_clipdata->pwcd_clipping, &rlold);
	rl_coalesce(&rlold);

	win_remove(fd);
	if (top) {
		win_setlink(fd, WL_COVERED, win_getlink(SunWindowSystem::rootFd, WL_TOPCHILD));
		win_setlink(fd, WL_COVERING, WIN_NULLLINK);
	} else {
		win_setlink(fd, WL_COVERING, win_getlink(SunWindowSystem::rootFd, WL_BOTTOMCHILD));
		win_setlink(fd, WL_COVERED, WIN_NULLLINK);
	}
	win_insert(fd);
	win_computeclipping(fd);

	pw_exposed(opw);
	rl_copy(&opw->pw_clipdata->pwcd_clipping, &rlnew);
	rl_coalesce(&rlnew);

	rl_intersection(&rlold, &rlnew, &opw->pw_clipdata->pwcd_clipping);

	win_getrect(fd, &rectnew);
	rectnew.r_left= rectnew.r_top= 0;

	for (rn= opw->pw_clipdata->pwcd_clipping.rl_head; rn; rn= rn->rn_next)
		win_partialrepair(fd, &rn->rn_rect);

	rl_free(&rlold);
	rl_free(&rlnew);

	win_unlockdata(fd);
}

Rectangle SunWindowPort::DevGetRect()
{
	if (overlay)
		return WindowPort::DevGetRect();
	struct rect s;
	win_getrect(fd, &s);
	return Rectangle(s.r_left, s.r_top, s.r_width, s.r_height);
}

static int buttons;

bool SunWindowPort::NextEvent(Token *t)
{
	bool b= NextEvent2(t);
	if (b) {
		if (t->Code == eEvtLeftButton
				|| t->Code == eEvtMiddleButton
						|| t->Code == eEvtRightButton) {
			int old= buttons;
			int bu= t->Code-eEvtButtons;
			if (t->Flags & eFlgButDown) {
				buttons&= ~(1 << bu);
			} else {
				buttons|= (1 << bu);
			}
			//fprintf(stderr, "%d %d %d\n", buttons&1, (buttons&2)>>1,
			//                                                (buttons&4)>>2);
			if (old != buttons) {
				if (old == 0 && buttons != 0)
					win_grabio(fd);
				if (old != 0 && buttons == 0)
					win_releaseio(fd);
			}
		}
	}
	return b;
}
		
bool SunWindowPort::NextEvent2(Token *t)
{
	inputevent e, *ie= &e;
	bool firstpfk= TRUE;

	FlushColorMap();

Restart:
	t->Code= -1;
	t->Flags= 0;

	if (myread(fd, ie))
		return FALSE;    /* no events in queue */
		
	t->Code= event_id(ie);
	t->At= event_time(ie).tv_sec * 1000 + event_time(ie).tv_usec / 1000;
	t->Pos= Point(event_x(ie), event_y(ie)) + inpOffset;

	if (event_shift_is_down(ie))
		t->Flags |= (int)eFlgShiftKey;
	if (event_ctrl_is_down(ie))
		t->Flags |= (int)eFlgCntlKey;
	if (event_meta_is_down(ie))
		t->Flags |= (int)eFlgMetaKey;
	if (win_inputnegevent(ie))
		t->Flags |= (int)eFlgButDown;
		
	if (t->Code == eEvtLocMoveBut) {
		t->Code= eEvtLocMove;
	} else if (t->Code >= eEvtPfkFirst && t->Code <= eEvtPfkLast) {
		if (firstpfk) {
			firstpfk= FALSE;
			goto Restart;
		} else
			t->Flags &= ~((int)eFlgButDown);
	}

	/* map cursor key escape sequences to one input token
	* (cannot map the keyboard, because mapping affects all windows !!)
	* check whether a cursor sequence (ESC '[' ( 'A' | 'B' | 'C' | 'D' ) ) occured
	*/
	if (t->Code == 033) {
		do {
			do {
				if (myread(fd, ie))
					return FALSE;
			} while (event_id(ie) == 033);  /* sometimes more ESC are received */

			if (event_id(ie) != '[') {
				t->Code= event_id(ie);
				return TRUE;
			}

			if (myread(fd, ie))
				return FALSE;
		} while (event_id(ie) == 033);

		switch (event_id(ie)) {
		case 'A':
			t->Code= (int) eEvtCursorUp;
			break;
		case 'B':
			t->Code= (int) eEvtCursorDown;
			break;
		case 'C':
			t->Code= (int) eEvtCursorRight;
			break;
		case 'D':
			t->Code= (int) eEvtCursorLeft;
			break;
		default :
			t->Code= event_id(ie);
			break;
		}
	}
	return TRUE;
}

bool SunWindowPort::Notify()
{
	SunWindowPort *p= SunWindowSystem::overlayport
			? SunWindowSystem::overlayport
			: this;
	if (p) {
		Token t;
		if (p->NextEvent(&t)) {
			p->InputNotify(&t);
			return TRUE;        // got one
		}
	}
	return FALSE;               // got nothing
}

void SunWindowPort::DevSetMousePos(const Point &p, bool m)
{
	_sunwindow_setmouse(fd, p.x, p.y, m);
}

void SunWindowPort::DevBell(long duration)
{
	_sunwindow_bell(fd, duration);
}

void SunWindowPort::DevSetPattern(Bitmap *b)
{
	DevBitmap *dbm= b->GetDevBitmap();
	patbm= (SunBitmap*) dbm->PrepareBitmap(this, dbm->size, b->GetColorMapper());
	color= FALSE;
	tmode= cmode= PIX_SRC;
	pat= GetPattern(gPoint0);
}

void SunWindowPort::DevSetOther(int code)
{
	patbm= 0;
	pat= 0;
	color= FALSE;

	switch (code) {
	case -1:
		tmode= PIX_SRC ^ PIX_DST;
		cmode= PIX_NOT(PIX_DST);
		break;
	case 0:
		tmode= cmode= PIX_DST;
		break;
	case 1:
		tmode= PIX_NOT(PIX_SRC) & PIX_DST;
		cmode= PIX_CLR;
		break;
	case 2:
		tmode= PIX_OR;
		cmode= PIX_SET;
		break;
	}
}

void SunWindowPort::DevSetColor2(u_long id)
{
	color= TRUE;
	patbm= 0;
	pat= 0;
	tmode= cmode= PIX_SRC | PIX_COLOR((int)id);
}

void SunWindowPort::DevSetTitle(const char *t)
{
	if (win && win->GetViewedVObject()->IsKindOf(WindowBorder))
		((WindowBorder*)win->GetViewedVObject())->SetTitle(t);
}

int SunWindowPort::DevGetId()
{
	return num;
}

//---- utilities

static bool myread(int fd, struct inputevent *ie)
{
	//File::SetNonBlock(fd);
	while (input_readevent(fd, ie) == -1) {
		if (errno == EWOULDBLOCK) {
			return TRUE;
		}
		if (errno == EINTR)
			continue;
		perror("myread");
		break;
	}
	return FALSE;
}

static bool MakeQuadrant(int radx, int rady, int gapx, int gapy, int h)
{
	static int lastradx, lastrady, lastgapx, lastgapy;
	register int d, m, n, t6, t7;
	int i, t1, t2, t3, t4, t5, t8;

	if (lastradx==radx && lastrady==rady && lastgapx==gapx && lastgapy==gapy)
		return FALSE;
	lastradx= radx; lastrady= rady;
	lastgapx= gapx; lastgapy= gapy;

	left0.size.x= right1.size.x= -radx;
	right0.size.x= left1.size.x= radx;
	left0.size.y= right0.size.y= left1.size.y= right1.size.y= rady;
	leftgapy.size.y= rightgapy.size.y= gapy;
	right_oct.pos.x= gapx;
	oval.y0= 0;
	oval.y1= h;
		
	radx--;
	rady--;

	t1= radx * radx; t2= t1 + t1;
	t3= rady * rady; t4= t3 + t3;
	t5= radx * t3; t6= t5 + t5; t7= 0;
	t8= radx + rady;
	d= (t1+t3)/4-t5;

	for (i= 0; i < 100; i++)
		shallowsteep[i]= steepshallow[i]= 0;

	setbit(shallowsteep, 0);
	for (m= 1, n= t8; m <= t8; m++, n--) {
		if (d < 0) {
			t7+= t2;
			d+= t7;
		} else {
			setbit(steepshallow, m);
			setbit(shallowsteep, n);
			t6-= t4;
			d-= t6;
		}
	}
	return TRUE;
}

static pixrect *MakePen(int psz)
{
	pixrect *pr= mem_create(psz, psz, 1);
	int gap, rad;

	rad= psz/2;
	gap= psz - 2*rad;
	MakeQuadrant(rad, rad, gap, gap, psz);
	pr_traprop(pr, rad, 0, oval, PIX_SET|PIX_DONTCLIP, 0, 0, 0);
	return pr;
}

